#ifndef INTERP_INTERNAL_HH
#define INTERP_INTERNAL_HH

#include <algorithm>
#include <strings.h>
#include "config.h"
#include <limits.h>
#include <stdio.h>
#include <set>
#include <map>
#include <bitset>
#include "nml_intf/canon.hh"
#include "zuc/nml_intf/zucpos.h"
#include "libintl.h"
#include <cmath>
#include <vector>
#include <string>
#include "errcode.h"
#include <stack>
#include <string>
#include <boost/smart_ptr.hpp>

#define _(s) gettext(s)

/**********************/
/*   COMPILER MACROS  */
/**********************/

template <class T> T R2D(T r) { return r * (180. / M_PI); }
template <class T> T D2R(T r) { return r * (M_PI / 180.); }
template <class T> T SQ(T a) { return a * a; }

template <class T> inline int round_to_int(T x) { return (int)std::nearbyint(x); }

/* how far above hole bottom for rapid return, in inches */
#define G83_RAPID_DELTA 0.010

/* nested remap: a remapped code is found in the body of a subroutine
 * which is executing on behalf of another remapped code
 * example: a user G code command executes a tool change
 */
#define MAX_NESTED_REMAPS 10

// English - Metric conversion (long number keeps error buildup down)
#define MM_PER_INCH 25.4
//#define INCH_PER_MM 0.039370078740157477

/* numerical constants */

/*****************************************************************************
The default tolerance (if none tighter is specified in the ini file) should be:
2 * 0.001 * sqrt(2) for inch, and 2 * 0.01 * sqrt(2) for mm.
This would mean that any valid arc where the endpoints and/or centerpoint
got rounded or truncated to 0.001 inch or 0.01 mm precision would be accepted.

Tighter tolerance down to a minimum of 1 micron +- also accepted.
******************************************************************************/

#define CENTER_ARC_RADIUS_TOLERANCE_INCH (2 * 0.001 * M_SQRT2)
#define MIN_CENTER_ARC_RADIUS_TOLERANCE_INCH 0.00004

// Note: started from original tolerance and divided by 10 here (since that was originally done inside the interpreter)
#define RADIUS_TOLERANCE_INCH 0.00005

/* Equivalent metric constants */

#define CENTER_ARC_RADIUS_TOLERANCE_MM (2 * 0.01 * M_SQRT2)
#define MIN_CENTER_ARC_RADIUS_TOLERANCE_MM 0.001

#define RADIUS_TOLERANCE_MM (RADIUS_TOLERANCE_INCH * MM_PER_INCH)

// Modest relative error
#define SPIRAL_RELATIVE_TOLERANCE 0.001

/* angle threshold for concavity for cutter compensation, in radians */
#define TOLERANCE_CONCAVE_CORNER 0.05
#define TOLERANCE_EQUAL \
    0.0001 /* two numbers compare EQ if the
				  difference is less than this */

static inline bool equal(double a, double b) { return (fabs(a - b) < TOLERANCE_EQUAL); }

#define TINY 1e-12 /* for arc_data_r */

// max number of m codes on one line
#define MAX_EMS 4

// feed_mode
enum feed_mode
{
    UNITS_PER_MINUTE = 0,
    INVERSE_TIME = 1,
    UNITS_PER_REVOLUTION = 2
};

// cutter radius compensation mode, 0 or false means none
// not using CANON_SIDE since interpreter handles cutter radius comp
#define RIGHT 1
#define LEFT 2

// spindle control modes
enum SPINDLE_MODE
{
    CONSTANT_RPM,
    CONSTANT_SURFACE
};

// unary operations
// These are not enums because the "&" operator is used in
// reading the operation names and is illegal with an enum

#define ABS 1
#define ACOS 2
#define ASIN 3
#define ATAN 4
#define COS 5
#define EXP 6
#define FLOOR 7
#define CEIL 8
#define LN 9
#define ROUND 10
#define SIN 11
#define SQRT 12
#define TAN 13
#define EXISTS 14
#define ATAN2 15

#define GETDOUT 20
#define GETAOUT 21
#define GETDIN 22
#define GETAIN 23
#define POSEADD 25
#define POSESUB 26
#define POSEINV 27
#define POSEDIST 28
#define POSETRANS 29
#define POSEINTPL 30
#define XYPLANETRANS 31
#define YZPLANETRANS 32
#define ZXPLANETRANS 33
#define VISIONLOC 40
#define GETTIMEOUT 50
#define GETATLJOINTPOSE 51
#define GETATLTCPPOSE 52
#define GETLENGTH 53
#define GETTCPOFFSETSOF 54
#define GETTCPOFFSETS 55
#define GETUSEROFFSETSOF 56
#define GETUSEROFFSETS 57
#define GETATLFLANGEPOSE 58
#define GETSENSORTORQUE 59

#define RV3DGETPOS 60
#define RV3DGETPOSCNT 61
#define SOCKETOPEN 80
#define SOCKETGETVAR 81
#define SOCKETCLOSE 82
#define SOCKETRECV 83
#define SOCKETREADREAL 85
#define SOCKETREADSTRING 86
#define SOCKETSEND 87
#define SOCKETSENDBYTE 88
#define STRINGCONCAT 100
#define SPRINTF 101
#define SSCANF 102
#define GETARRAYFROMSTRING 103
#define GETSTRINGFROMARRAY 104
#define GETARRAYELEMENT 110
#define SETARRAYELEMENT 111
#define STRINGCMP 112
#define GETSYSCLOCK 113
#define GETCLSNSENSITIVITY 114
#define GETPAYLOAD 115
#define GETTIOSIGNALVAL 116

#define KININVERSE 200
#define KINFORWARD 201
#define POSEREACHABLE 202

// binary operations
#define NO_OPERATION 0
#define DIVIDED_BY 1
#define MODULO 2
#define POWER 3
#define TIMES 4
#define AND2 5
#define EXCLUSIVE_OR 6
#define MINUS 7
#define NON_EXCLUSIVE_OR 8
#define PLUS 9
#define RIGHT_BRACKET 10
#define RIGHT_COMMA 11
#define RIGHT_SBRACKET 12

/* relational operators (are binary operators)*/
#define LT 13
#define EQ 14
const uint8_t NE = 15;
// #define NE (15)
#define LE 16
#define GE 17
#define GT 18
#define RELATIONAL_OP_FIRST 19
#define RELATIONAL_OP_LAST 20

// O code
#define O_none 0
#define O_sub 1
#define O_endsub 2
#define O_call 3
#define O_do 4
#define O_while 5
#define O_if 6
#define O_elif 7
#define O_else 8
#define O_endif 9
#define O_break 10
#define O_continue 11
#define O_endwhile 12
#define O_return 13
#define O_repeat 14
#define O_endrepeat 15

// G Codes are symbolic to be dialect-independent in source code
#define G_0 0
#define G_1 10
#define G_2 20
#define G_3 30
#define G_4 40
#define G_5 50
#define G_5_1 51
#define G_5_2 52
#define G_5_3 53
#define G_7 70
#define G_8 80
#define G_10 100
#define G_17 170
#define G_17_1 171
#define G_18 180
#define G_18_1 181
#define G_19 190
#define G_19_1 191
#define G_20 200
#define G_21 210
#define G_28 280
#define G_28_1 281
#define G_30 300
#define G_30_1 301
#define G_33 330
#define G_33_1 331
#define G_38_2 382
#define G_38_3 383
#define G_38_4 384
#define G_38_5 385
#define G_40 400
#define G_41 410
#define G_41_1 411
#define G_42 420
#define G_42_1 421
#define G_43 430
#define G_43_1 431
#define G_43_2 432
#define G_49 490
#define G_50 500
#define G_51 510
#define G_52 520
#define G_53 530
#define G_54 540
#define G_55 550
#define G_56 560
#define G_57 570
#define G_58 580
#define G_59 590
#define G_59_1 591
#define G_59_2 592
#define G_59_3 593
#define G_61 610
#define G_61_1 611
#define G_64 640
#define G_73 730
#define G_74 740
#define G_76 760
#define G_80 800
#define G_81 810
#define G_82 820
#define G_83 830
#define G_84 840
#define G_85 850
#define G_86 860
#define G_87 870
#define G_88 880
#define G_89 890
#define G_90 900
#define G_90_1 901
#define G_91 910
#define G_91_1 911
#define G_92 920
#define G_92_1 921
#define G_92_2 922
#define G_92_3 923
#define G_93 930
#define G_94 940
#define G_95 950
#define G_96 960
#define G_97 970
#define G_98 980
#define G_99 990
#define JK_MOVJ 1000
#define JK_MOVL 1001
#define JK_MOVC 1002
#define JK_SLEEP 1003
#define JK_SETTOOL 1004
#define JK_SETTOOLID 1005
#define JK_SETUSERFRAME 1006
#define JK_SETUSERFRAMEID 1007
#define JK_SETPAYLOAD 1008
#define JK_SETCOLLISIONLEVEL 1009
#define JK_MOVS 1010
#define JK_SETADMITCTRLCONFIG 1011
#define JK_SETCOMPLIANTCON 1012
#define JK_SETCOMPLIANTTYPE 1013
#define JK_SETVELCOMPLIANTLEVEL 1014
#define JK_ENA_ADMIT 1015
#define JK_SETFORCECONTROLFRAME 1016
#define JK_DISABLEFORCECONTROL 1017
#define JK_SETENDFORCECONDITION 1018
#define JK_PAUSE 1019
#define JK_EXIT 1020
#define JK_CONVEYOR_LINEAR_ENABLE 1021
#define JK_CONVEYOR_DISABLE 1022
#define JK_TIO_UPDATE_SIGNAL 1023
#define JK_TIO_SEND_COMMAND 1024
#define JK_CREATE_THREAD 1025
#define JK_CONVEYOR_CIRCULAR_ENABLE 1026
#define JK_RESUME 1027
#define JK_CLOSE_SPEED_PLANNER 1028

#define JK_DOUT 1100
#define JK_AOUT 1101
#define JK_WAITINPUT 1102

#define JK_VISIONBASE 1200  // maybe better in M mode

// name of parameter file for saving/restoring interpreter variables
#define RS274NGC_PARAMETER_FILE_NAME_DEFAULT "rs274ngc.var"
#define RS274NGC_PARAMETER_FILE_BACKUP_SUFFIX ".bak"

// number of parameters in parameter table

// leave some room above 5428 for further introspection
// 5599 = control DEBUG, output, 0=no output; default=1.0
// 5600-5601 = toolchanger codes
#define RS274NGC_MAX_PARAMETERS 5602

// Subroutine parameters
#define INTERP_SUB_PARAMS 30
#define INTERP_SUB_ROUTINE_LEVELS 10
#define INTERP_FIRST_SUBROUTINE_PARAM 1

// max number of local variables saved (?)
#define MAX_NAMED_PARAMETERS 50

/**********************/
/*      TYPEDEFS      */
/**********************/

/* distance_mode */
typedef enum
{
    MODE_ABSOLUTE,
    MODE_INCREMENTAL
} DISTANCE_MODE;

/* retract_mode for cycles */
typedef enum
{
    R_PLANE,
    OLD_Z
} RETRACT_MODE;

// string table - to get rid of strdup/free
const char* strstore(const char* s);

// Block execution phases in execution order
// very carefully check code for sequencing when
// adding phases!

// used to record execution trail in breadcrumbs
enum phases
{
    NO_REMAPPED_STEPS,
    STEP_COMMENT,
    STEP_SPINDLE_MODE,
    STEP_FEED_MODE,
    STEP_SET_FEED_RATE,
    STEP_SET_SPINDLE_SPEED,
    STEP_PREPARE,
    STEP_M_5,
    STEP_M_6,
    STEP_RETAIN_G43,
    STEP_M_7,
    STEP_M_8,
    STEP_M_9,
    STEP_M_10,
    STEP_DWELL,
    STEP_SET_PLANE,
    STEP_LENGTH_UNITS,
    STEP_LATHE_DIAMETER_MODE,
    STEP_CUTTER_COMP,
    STEP_TOOL_LENGTH_OFFSET,
    STEP_COORD_SYSTEM,
    STEP_CONTROL_MODE,
    STEP_DISTANCE_MODE,
    STEP_IJK_DISTANCE_MODE,
    STEP_RETRACT_MODE,
    STEP_MODAL_0,
    STEP_MOTION,
    STEP_MGROUP4,
    MAX_STEPS
};

typedef struct remap_struct remap;
typedef remap* remap_pointer;

// the remap configuration descriptor
typedef struct remap_struct
{
    const char* name;
    const char* argspec;
    // if no modalgroup= was given in the REMAP= line, use these defaults
#define MCODE_DEFAULT_MODAL_GROUP 10
#define GCODE_DEFAULT_MODAL_GROUP 1
    int modal_group;
    int motion_code;          // only for g's - to identify cycles
    const char* prolog_func;  // Py function or null
    const char* remap_py;     // Py function maybe  null, OR
    const char* remap_ngc;    // NGC file, maybe  null
    const char* epilog_func;  // Py function or null
} remap;

// case insensitive compare for std::map etc
struct nocase_cmp
{
    bool operator()(const char* s1, const char* s2) const { return strcasecmp(s1, s2) < 0; }
};

typedef std::map<const char*, remap, nocase_cmp> remap_map;
typedef remap_map::iterator remap_iterator;

typedef std::map<int, remap_pointer> int_remap_map;
typedef int_remap_map::iterator int_remap_iterator;

#define REMAP_FUNC(r) (r->remap_ngc ? r->remap_ngc : (r->remap_py ? r->remap_py : "BUG-no-remap-func"))

typedef struct End_Cond
{
    int di_type;
    int di_index;
    int di_state;
} End_Cond;

typedef struct block_struct
{
    block_struct();

    bool a_flag;
    double a_number;
    bool b_flag;
    double b_number;
    bool c_flag;
    double c_number;
    char comment[256];
    double d_number_float;
    bool d_flag;
    bool e_flag;
    double e_number;
    bool f_flag;
    double f_number;

// Modal groups
// also indices into g_modes
// unused: 9,11
#define GM_MODAL_0 0
#define GM_MOTION 1
#define GM_SET_PLANE 2
#define GM_DISTANCE_MODE 3
#define GM_IJK_DISTANCE_MODE 4
#define GM_FEED_MODE 5
#define GM_LENGTH_UNITS 6
#define GM_CUTTER_COMP 7
#define GM_TOOL_LENGTH_OFFSET 8
#define GM_RETRACT_MODE 10
#define GM_COORD_SYSTEM 12
#define GM_CONTROL_MODE 13
#define GM_SPINDLE_MODE 14
#define GM_LATHE_DIAMETER_MODE 15

    int robot_id;

    int g_modes[16];
    bool h_flag;
    int h_number;
    bool i_flag;
    double i_number;
    bool j_flag;
    double j_number;
    bool k_flag;
    double k_number;
    int l_number;
    bool l_flag;
    int line_number;
    int saved_line_number;  // value of sequence_number when a remap was encountered
    int n_number;
    int motion_to_be;
    int m_count;
    int m_modes[12];
    int user_m;
    double p_number;
    bool p_flag;
    double q_number;
    bool q_flag;
    bool r_flag;
    double r_number;
    bool s_flag;
    double s_number;
    bool t_flag;
    int t_number;
    bool u_flag;
    double u_number;
    bool v_flag;
    double v_number;
    bool w_flag;
    double w_number;
    bool x_flag;
    double x_number;
    bool y_flag;
    double y_number;
    bool z_flag;
    double z_number;

    int wait_di_type;
    int wait_di_index;
    int wait_di_stat;  // immediate, rising, fall, high, low
    float wait_di_timeout;

    double cmd_pos[ZUCMOT_MAX_JOINTS];  // cmd position, joint or cartesian as per command type
    double cmd_vel;                     // for movj and movl only
    double acc;
    double jerk;
    int planner_type;
    double ori_vel;    // for movl only
    double ori_acc;    // for movl only
    double tolerance;  // tolerance for this block

    int radius_flag;
    double radius;
    int theta_flag;
    double theta;

    // control (o-word) stuff
    long offset;  // start of line in file
    int o_type;
    int call_type;       // oword-sub, python oword-sub, remap
    const char* o_name;  // !!!KL be sure to free this
    double params[INTERP_SUB_PARAMS];
    int param_cnt;

    // bitmap of phases already executed
    // we have some 31 or so different steps in a block. We must remember
    // which one is done when we reexecute a block after a remap.
    std::bitset<MAX_STEPS> breadcrumbs;

    int rel_motion_flag;
    double movc_mid_point[6];
    double movc_end_point[6];
    double movs_a0[6];
    double movs_a1[6];
    double movs_a2[6];
    double movs_a3[6];
    double movs_time;
    double sleep_time;      // sleep time
    double tool_offset[6];  // tool offset
    double user_frame[6];   // user frame
    double conveyor_pulse_equivalent;
    int rotate_tool;          // for circular conveyor tracking; 0: tool gesture unchanged, 1: tool gesture changed
    double convyr_p1_pos[3];  //for circle conveyor belt types
    double convyr_p2_pos[3];
    double convyr_p3_pos[3];
    double conveyor_max_distance;
    double movcmode;      // for movc only 0-standard 1-midposeref 2-fixed
    int tool_id;          //set tool_id
    int user_id;          //set user_id
    ZucPose visnBasePos;  // vision base position

    int ioType;
    int ioIndex;            // the index for dout/aout
    bool out_imme_flag;     // whether executed immediately, or excuted with the next motion command
    bool dout_tar_state;    // the target output state of the digital output
    double aout_tar_value;  // tha target output value of the analog output

#define TICKOFF(step) block->breadcrumbs[step] = 1
#define TODO(step) (block->breadcrumbs[step] == 0)
#define ONCE(step) (TODO(step) ? TICKOFF(step), 1 : 0)
#define ONCE_M(step) (TODO(STEP_M_##step) ? TICKOFF(STEP_M_##step), 1 : 0)

    // there might be several remapped items in a block, but at any point
    // in time there's only one excuting
    // conceptually blocks[1..n] are also the 'remap frames'
    remap_pointer executing_remap;  // refers to config descriptor
    std::set<int> remappings;       // all remappings in this block (enum phases)
    int phase;                      // current remap execution phase
    // the strategy to get the builtin behaviour of a code in a remap procedure is as follows:
    // if recursion is detected in find_remappings() (called by parse_line()), that *step*
    // (roughly the modal group) is NOT added to the set of remapped steps in a block (block->remappings)
    // in the convert_* procedures we test if the step is remapped with the macro below, and wether
    // it is the current code which is remapped (IS_USER_MCODE, IS_USER_GCODE etc). If both
    // are true, we execute the remap procedure; if not, use the builtin code.
#define STEP_REMAPPED_IN_BLOCK(bp, step) (bp->remappings.find(step) != bp->remappings.end())
    // true if in a remap procedure the code being remapped was
    // referenced, which caused execution of the builtin semantics
    // reason for recording the fact: this permits an epilog to do the
    // right thing depending on wether the builtin was used or not.
    bool builtin_used;
    End_Cond condition;
    int last_mtn_end_cond;  // end condition enabled for last move, stop parsing
    int admit_enable;

    int utilId;        // collision level etc.
    int utilNum;       // collision level etc.
    int utilType;      // collision level etc.
    double utilFloat;  // common float variable
    Payload payload;
    FtConfig ftConfig;  // admit control config
    double compliantCondition[6];
    int typeEna[2];
    double velCompliantCtrl[5];
    int ftFrame;
    int disableForceCtrl;
    EndForceCond endForceCond;
    double count;
    short from_thread;
} block;

enum call_states
{
    CS_NORMAL
};

// detail for O_call; tags the frame
enum call_types
{
    CT_NGC_OWORD_SUB  // no restartable Python code involved
};

enum retopts
{
    RET_NONE,
    RET_DOUBLE,
    RET_INT,
    RET_YIELD,
    RET_STOPITERATION,
    RET_ERRORMSG
};

typedef block* block_pointer;

// parameters will go to a std::map<const char *,paramter_value_pointer>
#define SCALAR_VALUE 0
#define ARRAY_VALUE 1
#define STRING_VALUE 2

class jaka_value
{
public:
    jaka_value() : valueType(SCALAR_VALUE), scalarValue(0.0), strValue("") { arrValue.clear(); }

    jaka_value(const jaka_value& v)
    {
        valueType = v.valueType;
        scalarValue = v.scalarValue;
        arrValue = v.arrValue;
        strValue = v.strValue;
    }

    jaka_value& operator=(const jaka_value& v)
    {
        this->valueType = v.valueType;
        this->scalarValue = v.scalarValue;
        this->arrValue = v.arrValue;
        this->strValue = v.strValue;
        return *this;
    }
    jaka_value& operator=(const double& fval)
    {
        this->valueType = SCALAR_VALUE;
        this->scalarValue = fval;
        this->arrValue.clear();
        this->strValue = "";
        return *this;
    }
    jaka_value& operator=(const std::vector<double>& vec)
    {
        this->valueType = ARRAY_VALUE;
        this->scalarValue = 0;
        this->arrValue = vec;
        this->strValue = "";
        return *this;
    }

    jaka_value& operator=(const std::string& str)
    {
        this->valueType = STRING_VALUE;
        this->scalarValue = 0;
        this->arrValue.clear();
        this->strValue = str;
        return *this;
    }

    ~jaka_value()
    {
        if (!arrValue.empty())
            arrValue.clear();
    }

public:
    short valueType;  // 0 --> scalar; 1 --> array; 2 --> string
    double scalarValue;
    std::vector<double> arrValue;
    std::string strValue;
};

// typedef struct value_struct{
// 	value_struct()
// 	{
//         valueType = 0;
// 		scalarValue = 0;
// 		// arrValue = NULL;
// 		// arrSize = 0;
// 	}
// 	~value_struct()
// 	{
//         if(!arrValue.empty())
//             arrValue.clear();
// 		// if(arrValue != NULL)
// 		// {
// 		// 	delete [] arrValue;
// 		// 	arrValue = NULL;
// 		// }
// 	}

//     short valueType;	// 0 --> scalar; 1 --> array
//     double scalarValue;
//     std::vector<double> arrValue;
// 	// double* arrValue;
// 	// int arrSize;
// }jaka_value;

typedef struct parameter_value_struct
{
    unsigned attr;
    jaka_value value;
} parameter_value;

typedef parameter_value* parameter_pointer;
typedef std::map<const char*, parameter_value, nocase_cmp> parameter_map;
typedef parameter_map::iterator parameter_map_iterator;

#define PA_READONLY 1
#define PA_GLOBAL 2
#define PA_UNSET 4
#define PA_USE_LOOKUP 8  // use lookup_named_param() to retrieve value
#define PA_FROM_INI 16   // a variable of the form '_[section]value' was retrieved from the ini file
#define PA_PYTHON 32     // call namedparams.<varname>() to retrieve the value

// optional 3rd arg to store_named_param()
// flag initialization of r/o parameter
#define OVERRIDE_READONLY 1

#define MAX_REMAPOPTS 20
// current implementation limits - legal modal groups
// for M and G codes
#define M_MODE_OK(m) ((m > 3) && (m < 11))
#define G_MODE_OK(m) (m == 1)

struct pycontext_impl;
struct pycontext
{
    pycontext();
    pycontext(const struct pycontext&);
    pycontext& operator=(const struct pycontext&);
    ~pycontext();
    pycontext_impl* impl;
};

typedef struct context_struct
{
    context_struct();

    long position;         // location (ftell) in file
    int sequence_number;   // location (line number) in file
    const char* filename;  // name of file for this context
    const char* subName;   // name of the subroutine (oword)
    double saved_params[INTERP_SUB_PARAMS];
    parameter_map named_params;
    unsigned char context_status;  // see CONTEXT_ defines below
    int call_type;                 // enum call_types
} context;

typedef context* context_pointer;

// context.context_status
#define CONTEXT_VALID 1              // this was stored by M7*
#define CONTEXT_RESTORE_ON_RETURN 2  // automatically execute M71 on sub return
#define REMAP_FRAME 4                // a remap call frame

typedef struct offset_struct
{
    int type;
    const char* filename;  // the name of the file
    long offset;           // the offset in the file
    int sequence_number;
    int repeat_count;
} offset;

typedef std::multimap<const char*, offset, nocase_cmp> offset_map_type;
typedef std::multimap<const char*, offset, nocase_cmp>::iterator offset_map_iterator;

/*

The current_x, current_y, and current_z are the location of the tool
in the current coordinate system. current_x and current_y differ from
program_x and program_y when cutter radius compensation is on.
current_z is the position of the tool tip in program coordinates when
tool length compensation is using the actual tool length; it is the
position of the spindle when tool length is zero.

In a setup, the axis_offset values are set by g92 and the origin_offset
values are set by g54 - g59.3. The net origin offset uses both values
and is not represented here

*/
#define STACK_LEN 50
#define STACK_ENTRY_LEN 200
#define MAX_SUB_DIRS 10

typedef struct
{
    FILE* file_pointer;
    long lastLine;
    block blocks[MAX_NESTED_REMAPS];
    int sequence_number;
    std::string filename, newFilename;
    offset_map_type offsetMap;
    std::vector<std::pair<int, std::string>> owordMap;
} SubFileMsg;

struct setup
{
    setup();
    ~setup();

    double AA_current;  // current A-axis position
    double BB_current;  // current B-axis position
    double CC_current;  // current C-axis position
    double u_current;
    double v_current;
    double w_current;
    int active_g_codes[ACTIVE_G_CODES];       // array of active G codes
    int active_m_codes[ACTIVE_M_CODES];       // array of active M codes
    double active_settings[ACTIVE_SETTINGS];  // array of feed, speed, etc.
    bool arc_not_allowed;                     // we just exited cutter compensation, so we error if the next move isn't straight

    // stack of controlling blocks for remap execution
    block blocks[MAX_NESTED_REMAPS];
    // index into blocks, points to currently controlling block
    int remap_level;

#define CONTROLLING_BLOCK(s) ((s).blocks[(s).remap_level])
#define EXECUTING_BLOCK(s) ((s).blocks[0])

    char blocktext[LINELEN];         // linetext downcased, white space gone
    CANON_MOTION_MODE control_mode;  // exact path or cutting mode
    int current_pocket;              // carousel slot number of current tool
    double current_x;                // current X-axis position
    double current_y;                // current Y-axis position
    double current_z;                // current Z-axis position
    double cutter_comp_radius;       // current cutter compensation radius
    int cutter_comp_orientation;     // current cutter compensation tool orientation
    int cutter_comp_side;            // current cutter compensation side
    double cycle_cc;                 // cc-value (normal) for canned cycles
    double cycle_i;                  // i-value for canned cycles
    double cycle_j;                  // j-value for canned cycles
    double cycle_k;                  // k-value for canned cycles
    int cycle_l;                     // l-value for canned cycles
    double cycle_p;                  // p-value (dwell) for canned cycles
    double cycle_q;                  // q-value for canned cycles
    double cycle_r;                  // r-value for canned cycles
    double cycle_il;                 // "initial level" height when switching from non-cycle into cycle, for g98 retract
    int cycle_il_flag;               // il is currently valid because we're in a series of cycles
    DISTANCE_MODE distance_mode;     // absolute or incremental

    DISTANCE_MODE ijk_distance_mode;  // absolute or incremental for IJK in arcs
    int feed_mode;                    // G_93 (inverse time) or G_94 units/min
    bool feed_override;               // whether feed override is enabled
    double feed_rate;                 // feed rate in current units/min
    char filename[PATH_MAX];          // name of currently open NC code file
    FILE* file_pointer;               // file pointer for open NC code file
    bool flood;                       // whether flood coolant is on
    CANON_UNITS length_units;         // millimeters or inches
    int line_length;                  // length of line last read
    char linetext[LINELEN];           // text of most recent line read
    bool mist;                        // whether mist coolant is on
    int motion_mode;                  // active G-code for motion
    int origin_index;                 // active origin (1=G54 to 9=G59.3)
    double* parameters;               // system parameters
    int sub_thread_para_flag = 0;
    int m_threadId;
    int parameter_occurrence;                       // parameter buffer index
    int parameter_numbers[MAX_NAMED_PARAMETERS];    // parameter number buffer
    double parameter_values[MAX_NAMED_PARAMETERS];  // parameter value buffer
    int named_parameter_occurrence;
    const char* named_parameters[MAX_NAMED_PARAMETERS];
    jaka_value named_parameter_values[MAX_NAMED_PARAMETERS];
    bool percent_flag;                               // true means first line was percent sign
    CANON_PLANE plane;                               // active plane, XY-, YZ-, or XZ-plane
    bool input_flag;                                 // flag indicating waiting for input done
    bool toolchange_flag;                            // flag indicating we just had a tool change
    int input_index;                                 // channel queried
    bool input_digital;                              // input queried was digital (false=analog)
    bool cutter_comp_firstmove;                      // this is the first comp move
    double program_x;                                // program x, used when cutter comp on
    double program_y;                                // program y, used when cutter comp on
    double program_z;                                // program y, used when cutter comp on
    RETRACT_MODE retract_mode;                       // for cycles, old_z or r_plane
    int random_toolchanger;                          // tool changer swaps pockets, and pocket 0 is the spindle instead of "no tool"
    int selected_pocket;                             // tool slot selected but not active
    int selected_tool;                               // start switchover to pocket-agnostic interp
    int sequence_number;                             // sequence number of line last read
    int current_line;                                //
    double speed;                                    // current spindle speed in rpm or SxM
    SPINDLE_MODE spindle_mode;                       // CONSTANT_RPM or CONSTANT_SURFACE
    CANON_SPEED_FEED_MODE speed_feed_mode;           // independent or synched
    CANON_DIRECTION spindle_turning;                 // direction spindle is turning
    char stack[STACK_LEN][STACK_ENTRY_LEN];          // stack of calls for error reporting
    int stack_index;                                 // index into the stack
    ZucPose user_offset;                             // tool length offset
    ZucPose tool_offset;                             // robot tool offset
    int pockets_max;                                 // number of pockets in carousel (including pocket 0, the spindle)
    CANON_TOOL_TABLE tool_table[CANON_POCKETS_MAX];  // index is pocket number
    double traverse_rate;                            // rate for traverse motions
    double orient_offset;                            // added to M19 R word, from [RS274NGC]ORIENT_OFFSET

    /* stuff for subroutines and control structures */
    int defining_sub;      // true if in a subroutine defn
    const char* sub_name;  // name of sub we are defining (free this)
    int doing_continue;    // true if doing a continue
    int doing_break;       // true if doing a break
    int executed_if;       // true if executed in current if
    int doing_else;        // true if executing a else
    int doing_elif;        // true if executing a elif

    const char* skipping_o;       // o_name we are skipping for (or zero)
    const char* skipping_to_sub;  // o_name of sub skipping to (or zero)
    int skipping_start;           // start of skipping (sequence)
    double test_value;            // value for "if", "while", "elif"
    double return_value;          // optional return value for "return", "endsub"
    int value_returned;           // the last NGC procedure did/did not return a value
    int call_level;               // current subroutine level
    context sub_context[INTERP_SUB_ROUTINE_LEVELS];
    int call_state;              //  enum call_states - inidicate Py handler reexecution
    offset_map_type offset_map;  // store label x name, file, line

    int loggingLevel;  // 0 means logging is off
    int debugmask;     // from ini  ZUC/DEBUG
    char log_file[PATH_MAX];
    char program_prefix[PATH_MAX];          // program directory
    const char* subroutines[MAX_SUB_DIRS];  // subroutines directories
    int use_lazy_close;                     // wait until next open before closing
                                            // the input file
    int lazy_closing;                       // close has been called
    char wizard_root[PATH_MAX];
    int tool_change_at_g30;
    int tool_change_quill_up;
    int tool_change_with_spindle_on;
    int a_axis_wrapped;
    int b_axis_wrapped;
    int c_axis_wrapped;

    int a_indexer_jnum;
    int b_indexer_jnum;
    int c_indexer_jnum;

    bool lathe_diameter_mode;  //Lathe diameter mode (g07/G08)
    bool mdi_interrupt;
    int feature_set;

    int disable_g92_persistence;

#define FEATURE(x) (_setup.feature_set & FEATURE_##x)
#define FEATURE_RETAIN_G43 0x00000001
#define FEATURE_OWORD_N_ARGS 0x00000002
#define FEATURE_INI_VARS 0x00000004
#define FEATURE_HAL_PIN_VARS 0x00000008
    // do not lowercase named params inside comments - for #<_hal[PinName]>
#define FEATURE_NO_DOWNCASE_OWORD 0x00000010
#define FEATURE_OWORD_WARNONLY 0x00000020

    const char* on_abort_command;
    int_remap_map g_remapped, m_remapped;
    remap_map remaps;
#define INIT_FUNC "__init__"
#define DELETE_FUNC "__delete__"

    // task calls upon interp.init() repeatedly
    // protect init() operations which are not idempotent
    int init_once;

    /*joint position to support movj, by zxqi*/
    double joints_current[ZUCMOT_MAX_JOINTS];
    std::vector<SubFileMsg> sub_file_pointer;              //Storage file execution environment
    std::vector<std::string> sub_filename;                 //Store nested file name
    std::map<std::string, SubFileMsg> sub_program_offset;  //
    long lastLine;                                         // record the position of last line
    double center_arc_radius_tolerance_inch;               // modify with ini setting
    double center_arc_radius_tolerance_mm;                 // modify with ini setting
};

struct multi_thread_setup : setup
{
    double* parameters;
};

typedef setup* setup_pointer;
// the externally visible singleton instance

inline bool is_a_cycle(int motion) { return ((motion > G_80) && (motion < G_90)) || (motion == G_73) || (motion == G_74); }
/*

The _setup model includes a stack array for the names of function
calls. This stack is written into if an error occurs. Just before each
function returns an error code, it writes its name in the next
available string, initializes the following string, and increments
the array index. The following four macros do the work.

The size of the stack array is 50. An error in the middle of a very
complex expression would cause the ERP and CHP macros to write past the
bounds of the array if a check were not provided. No real program
would contain such a thing, but the check is included to make the
macros totally crash-proof. If the function call stack is deeper than
49, the top of the stack will be missing.

*/

// Just set an error string using printf-style formats, do NOT return
#define ERM(errcode, fmt, ...)                                                           \
    do {                                                                                 \
        setError(errcode, fmt, ##__VA_ARGS__);                                           \
        _setup.stack_index = 0;                                                          \
        strncpy(_setup.stack[_setup.stack_index], __PRETTY_FUNCTION__, STACK_ENTRY_LEN); \
        _setup.stack[_setup.stack_index][STACK_ENTRY_LEN - 1] = 0;                       \
        _setup.stack_index++;                                                            \
        _setup.stack[_setup.stack_index][0] = 0;                                         \
    } while (0)

// Set an error string using printf-style formats and return
#define ERS(errcode, fmt, ...)                                                           \
    do {                                                                                 \
        setError(errcode, fmt, ##__VA_ARGS__);                                           \
        _setup.stack_index = 0;                                                          \
        strncpy(_setup.stack[_setup.stack_index], __PRETTY_FUNCTION__, STACK_ENTRY_LEN); \
        _setup.stack[_setup.stack_index][STACK_ENTRY_LEN - 1] = 0;                       \
        _setup.stack_index++;                                                            \
        _setup.stack[_setup.stack_index][0] = 0;                                         \
        return INTERP_ERROR;                                                             \
    } while (0)

// Return one of the very few numeric errors
#define ERN(error_code)                                                                  \
    do {                                                                                 \
        _setup.stack_index = 0;                                                          \
        strncpy(_setup.stack[_setup.stack_index], __PRETTY_FUNCTION__, STACK_ENTRY_LEN); \
        _setup.stack[_setup.stack_index][STACK_ENTRY_LEN - 1] = 0;                       \
        _setup.stack_index++;                                                            \
        _setup.stack[_setup.stack_index][0] = 0;                                         \
        return error_code;                                                               \
    } while (0)

// Propagate an error up the stack
#define ERP(error_code)                                                                      \
    do {                                                                                     \
        if (_setup.stack_index < STACK_LEN - 1)                                              \
        {                                                                                    \
            strncpy(_setup.stack[_setup.stack_index], __PRETTY_FUNCTION__, STACK_ENTRY_LEN); \
            _setup.stack[_setup.stack_index][STACK_ENTRY_LEN - 1] = 0;                       \
            _setup.stack_index++;                                                            \
            _setup.stack[_setup.stack_index][0] = 0;                                         \
        }                                                                                    \
        return error_code;                                                                   \
    } while (0)

// If the condition is true, set an error string as with ERS
#define CHKS(bad, errcode, fmt, ...)          \
    do {                                      \
        if (bad)                              \
        {                                     \
            ERS(errcode, fmt, ##__VA_ARGS__); \
        }                                     \
    } while (0)

// If the condition is true, return one of the few numeric errors
#define CHKN(bad, error_code) \
    do {                      \
        if (bad)              \
        {                     \
            ERN(error_code);  \
        }                     \
    } while (0)

// Propagate an error up the stack as with ERP if the result of 'call' is not
// INTERP_OK
#define CHP(call)                     \
    do {                              \
        int CHP__status = (call);     \
        if (CHP__status != INTERP_OK) \
        {                             \
            ERP(CHP__status);         \
        }                             \
    } while (0)

// oword warnings
#define OERR(fmt, ...)                                \
    do {                                              \
        if (FEATURE(OWORD_WARNONLY))                  \
            fprintf(stderr, fmt, ##__VA_ARGS__);      \
        else                                          \
            ERS(INTERP_ERR_TEMP, fmt, ##__VA_ARGS__); \
    } while (0)

//
// The traverse (in the active plane) to the location of the canned cycle
// is different on the first repeat vs on all the following repeats.
//
// The first traverse happens in the CURRENT_CC plane (which was raised to
// the R plane earlier, if needed), followed by a traverse down to the R
// plane.
//
// All later positioning moves happen in the CLEAR_CC plane, which is
// either the R plane or the OLD_CC plane depending on G98/G99.
//

#define CYCLE_MACRO(call)                                                                                                                             \
    for (repeat = block->l_number; repeat > 0; repeat--)                                                                                              \
    {                                                                                                                                                 \
        aa = (aa + aa_increment);                                                                                                                     \
        bb = (bb + bb_increment);                                                                                                                     \
        if (radius_increment)                                                                                                                         \
        {                                                                                                                                             \
            double radius, theta;                                                                                                                     \
            CHKS((bb == 0 && aa == 0), INTERP_ERR_SYNTAX_FAILED, _("Incremental motion with polar coordinates is indeterminate when at the origin")); \
            theta = atan2(bb, aa);                                                                                                                    \
            radius = hypot(bb, aa) + radius_increment;                                                                                                \
            aa = radius * cos(theta);                                                                                                                 \
            bb = radius * sin(theta);                                                                                                                 \
        }                                                                                                                                             \
        if (theta_increment)                                                                                                                          \
        {                                                                                                                                             \
            double radius, theta;                                                                                                                     \
            CHKS((bb == 0 && aa == 0), INTERP_ERR_SYNTAX_FAILED, _("Incremental motion with polar coordinates is indeterminate when at the origin")); \
            theta = atan2(bb, aa) + theta_increment;                                                                                                  \
            radius = hypot(bb, aa);                                                                                                                   \
            aa = radius * cos(theta);                                                                                                                 \
            bb = radius * sin(theta);                                                                                                                 \
        }                                                                                                                                             \
        if ((repeat == block->l_number) && (current_cc > r))                                                                                          \
        {                                                                                                                                             \
            cycle_traverse(block, plane, aa, bb, current_cc);                                                                                         \
            cycle_traverse(block, plane, aa, bb, r);                                                                                                  \
        }                                                                                                                                             \
        else                                                                                                                                          \
        {                                                                                                                                             \
            /* we must be at CLEAR_CC already */                                                                                                      \
            cycle_traverse(block, plane, aa, bb, clear_cc);                                                                                           \
            if (clear_cc > r)                                                                                                                         \
            {                                                                                                                                         \
                cycle_traverse(block, plane, aa, bb, r);                                                                                              \
            }                                                                                                                                         \
        }                                                                                                                                             \
        CHP(call);                                                                                                                                    \
    }

extern void splitString(const std::string& s, std::vector<std::string>& v, const std::string& c);

extern std::string replaceString(std::string str, const std::string& from, const std::string& to);

#endif  // INTERP_INTERNAL_HH
